//
// Created by bangsun on 2022/10/15.
//

#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "include/round_list.h"
#include "include/util.h"


PRIVATE node_id fetchID();


void init_round_list(round_list *roundList) {
    roundList->cur = NULL;
    roundList->total = 0;
}

void destroy_round_list(round_list *roundList) {
    ll_node *tmp;
    for (int i = 0; i < roundList->total; i++) {
        tmp = roundList->cur->next;
        free(tmp);
        roundList->cur = tmp;
    }
    free(roundList);
}


ll_node *insert_node(round_list *roundList, void *val) {
    ll_node *node = malloc(sizeof(ll_node));
    assert(node);
    node->id = fetchID();
    node->val = val;
    if (roundList->cur == NULL) {
        node->prev = node->next = node;
        roundList->cur = node;
    } else {
        node->prev = roundList->cur;
        node->next = roundList->cur->next;
        node->prev->next = node->next->prev = node;
    }
    ++roundList->total;
    return node;
}


ll_node *insert_directly(round_list *roundList, ll_node *node) {
    if (roundList->cur == NULL) {
        node->prev = node->next = node;
        roundList->cur = node;
    } else {
        node->prev = roundList->cur;
        node->next = roundList->cur->next;
        node->prev->next = node->next->prev = node;
    }
    ++roundList->total;
    return node;
}


ll_node *delete_node(round_list *list, node_id id) {
    ll_node *tmp = list->cur;
    for (int i = 0; i < list->total; i++) {
        if (tmp->id == id) {
            if (list->total > 1) {
                list->cur = tmp->next;
                tmp->prev->next = tmp->next;
                tmp->next->prev = tmp->prev;
            } else {
                list->cur = NULL;
            }
            list->total--;
            return tmp;
        }
        tmp = tmp->next;
    }
    return NULL;
}

ll_node *delete_directly(round_list *list, ll_node *node) {
    if (list == NULL || list->cur == NULL || list->total < 1) {
        exit(1);
    }
    if (list->total == 1) {
        list->cur = NULL;
    } else {
        list->cur = node->next;
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
    list->total--;
    return node;
}


void init_sized_node_pool(sized_node_pool *nodePool, int capacity) {

    init_round_list(&nodePool->alive);
    init_round_list(&nodePool->dead);
    nodePool->capacity = capacity;
    nodePool->lookUpMap = malloc(capacity * sizeof(ll_node *));
}

void reset_size(sized_node_pool *nodePool, int newSize) {
    ll_node **newMap = malloc(newSize * sizeof(ll_node *));
    memcpy(nodePool->lookUpMap, newMap, nodePool->capacity * sizeof(ll_node *));
    nodePool->capacity = newSize;
    free(nodePool->lookUpMap);
    nodePool->lookUpMap = newMap;
}

void pool_insert_node(sized_node_pool *nodePool, node_id *tid, void *val) {
    if (nodePool->dead.total == 0) {// create new node
        ll_node *new_node = insert_node(&nodePool->alive, val);
        int new_id = new_node->id;
        if (new_id >= nodePool->capacity) {
            reset_size(nodePool, nodePool->capacity << 1);
            assert(nodePool->capacity > 0);
        }
        *tid = new_id;
        nodePool->lookUpMap[new_id] = new_node;
    } else {// reuse node from dead
        ll_node *reuse_node = poll_node(&nodePool->dead);
        reuse_node->val = val;
        insert_directly(&nodePool->alive, reuse_node);
        nodePool->lookUpMap[reuse_node->id] = reuse_node;
    }
}

void pool_delete_node(sized_node_pool *nodePool, node_id tid, void(*val_destroyer)(void *)) {
    ll_node *cur_node = nodePool->lookUpMap[tid];
    assert(cur_node);
    delete_directly(&nodePool->alive, cur_node);
    if (val_destroyer != NULL) {
        val_destroyer(cur_node->val);
    }
    insert_directly(&nodePool->dead, cur_node);
}

void pool_destroy_node(sized_node_pool *nodePool, node_id tid, void (*val_destroyer)(void *)) {
    ll_node *cur_node = nodePool->lookUpMap[tid];
    assert(cur_node);
    delete_directly(&nodePool->alive, cur_node);
    if (val_destroyer != NULL) {
        val_destroyer(cur_node->val);
    }
    free(cur_node);
}

void *pool_iter_node(sized_node_pool *pool, int exception) {
    ll_node *ret = pool->alive.cur;
    for (int i = 0; i < pool->alive.total; i++) {
        if (ret->id != exception) {
            pool->alive.cur = ret;
            return ret->val;
        }
        ret = ret->next;
    }
    return NULL;
}

void *pool_get_node(sized_node_pool *nodePool, node_id id) {
    ll_node *node = nodePool->lookUpMap[id];
    assert(node);
    return node->val;
}


node_id fetchID() {
    static int id = 0;
    return id++;
}


ll_node *poll_node(round_list *list) {
    if (list == NULL || list->total == 0 || list->cur == NULL) {
        exit(1);
    }
    ll_node *ret = list->cur;
    if (list->total == 1) {
        list->cur = NULL;
    } else {
        list->cur = list->cur->next;
        ret->prev->next = ret->next;
        ret->next->prev = ret->prev;
    }
    list->total--;
    return ret;
}




